/**
 * Copyright 1996 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 *
 * Note, this is a very old runtime library that I have evolved since 1996. I rarely use it
 * now because the Apache Commons project has made it obsolete.
 */
package self.oss.svn.util;

import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * Reponsible for converting between an SVN Date String and a Date.
 * <p>
 * This class provides the 2 main functions of a {@link DateFormat}
 * which is specific to the stringified date representation used by SubVersion.
 * </p>
 * 
 * <p>
 * I added this class to the self-rt library b/c I dont want any components other
 * having a dependency on on jni-svn-ra. -- ash 
 * </p>
 * 
 * Acknowledge Tmatesoftware for the origins of this code
 */
public class SvnDateFormat extends DateFormat {
	private static final long serialVersionUID = 1L;

	/**
	 * The Date format with as much precision as SimpleDateFormat can handle.
	 * <p>
	 * The SVN date Strings could be experessed as : 
	 * 		yyyy-MM-dd'T'HH:mm:ss.SSSXXX'Z'
	 * where XXX is the number of microseconds, if SimpleDateFormat handled microsconds.
	 * </p>
	 * <p>
	 * We need to remove the last 4 characters of the date strings so that the  
	 * extra microsecond digits at the end are removed.
	 * 
	 * If this is not done, then the SimpleDateFormat assumes that the 6 digits at the 
	 * end of the SVN date String are in fact all milliseconds and adds all the 
	 * microseconds as millisecond to the Date, causing the date to vary by up to 
	 * 16 minutes (1,000,000 milliseconds).
	 * </p>
	 * <p>  
	 * We could push the SVN date String parsing into our own DateFormat implementation,
	 * something which can interpret a microsecond tag and ignore those characters,
	 * but it didn't seem worth the effort.
	 * </p> 
	 */
	static private final DateFormat parsingFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
	
	/**
	 * Used to render an SVN date String from a Date.
	 */
	static private final DateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'000Z'");

	
	public Date parse(String svnDateString, ParsePosition pos) {
//		// Trim the svn date String so that the microseconds don't confuse SimpleDateFormat.
//		final int targetLength = svnDateString.length() - 4;
//		final String trimmedDateString = svnDateString.substring(0, targetLength);
//		try {
//			pos.setIndex(1);
//			return parsingFormat.parse(trimmedDateString);
//		} catch (ParseException err) {
//			pos.setIndex(0);
//			return null;
//		}
				
		long svnDateAsLong = parseDateAsLong(svnDateString);
		Date ret = new Date(svnDateAsLong);
        pos.setIndex(1);
		return ret;
	}

	public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
//		String ret = outputFormat.format(date);
//		toAppendTo.append(ret);
//		return toAppendTo;
        return ISO8601_FORMAT_OUT.format(date, toAppendTo, fieldPosition);
	}

	// portions borrowed from javasvn
    private static final DateFormat ISO8601_FORMAT_OUT = new SimpleDateFormat(
	"yyyy-MM-dd'T'HH:mm:ss.SSS'000Z'");

	static {
	    ISO8601_FORMAT_OUT.setTimeZone(TimeZone.getTimeZone("GMT"));
	}

    private static final Calendar CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
	
    private static final char[] DATE_SEPARATORS = {'-','-','T',':',':','.','Z'}; 

    public static long parseDateAsLong(String str) {
        if (str == null) {
            return -1;
        }
        int index = 0;
        int charIndex = 0;
        int startIndex = 0;
        int[] result = new int[7];
        while(index < DATE_SEPARATORS.length && charIndex < str.length()) {
            if (str.charAt(charIndex) == DATE_SEPARATORS[index]) {
                String segment = str.substring(startIndex, charIndex);
                if (segment.length() == 0) {
                    result[index] = 0;
                } else if (index + 1 < DATE_SEPARATORS.length) {
                    result[index] = Integer.parseInt(segment);
                } else {
                    result[index] = Integer.parseInt(segment.substring(0, Math.min(3, segment.length())));
                }
                startIndex = charIndex + 1;
                index++;
            }
            charIndex++;
        }
        int year = result[0];
        int month = result[1];
        int date = result[2];

        int hour = result[3];
        int min = result[4];
        int sec = result[5];
        int ms = result[6];

        CALENDAR.clear();
        CALENDAR.set(year, month - 1, date, hour, min, sec);
        CALENDAR.set(Calendar.MILLISECOND, ms);
        return CALENDAR.getTimeInMillis();
    }
    
    static public void main(String[] args) throws Exception {
        String testCaseStr = "2005-12-14T21:54:48.502488Z";
        long testCaseLong = 1134597288502L;
    	DateFormat fmt = new SvnDateFormat();
    	Date parseVal = fmt.parse(testCaseStr);
    	System.out.println("parse: " + (parseVal.getTime() == testCaseLong) );
    	String fmtVal = fmt.format(parseVal);
    	System.out.println("fmt: " + fmtVal.equals(testCaseStr) );
    	Date parse2 = fmt.parse(fmtVal);
    	System.out.println("out: " + fmtVal );
    	System.out.println("out: " + parse2.getTime() );
    }
}